/* -*- c -*- */

D               [0-9]
L               [a-zA-Z_]
H               [a-fA-F0-9]
E               [Ee][+-]?{D}+
FS              (f|F|l|L)
IS              (u|U|l|L)*
CHAR            '(\\.|[^\\'])+'
STR             \"(\\.|[^\\"])*\"
SPACE           [ \t\v\n\f]

%{

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sobjc-commons/defines.h>
#include <sobjc-commons/str_stream.h>
#include <sobjc-commons/str_list.h>
#include <sobjc-commons/utils.h>
#include "node.h"
#include "command.h"
#include "context.h"
#include "parse.h"
#include "scope.h"

#define LINE_MARK_BEGIN     0x01
#define LINE_MARK_END       0x02
#define LINE_MARK_SYSTEM    0x04
#define LINE_MARK_SRC       0x08

struct stack {
  unsigned int i;
  objc_str_list_t prefixes;
};

static void ignore_line();
static void comment();
static void comment_one_line();
static void count();
static int check_type();

static char *parsing_file;
static int parsing_lineno;
static int line_mark_flags;
static size_t lc = 0;

%}

%x LINEMARK_LINENO LINEMARK_FILE LINEMARK_FLAGS
%%
#[ \t\v\f]* {
  BEGIN(LINEMARK_LINENO);
  lc += strlen(yytext);
  line_mark_flags = 0;
}
<LINEMARK_LINENO>{D}+ {
  BEGIN(LINEMARK_FILE);
  lc += strlen(yytext);
  parsing_lineno = strtol(yytext, NULL, 10);
}
<LINEMARK_LINENO>. {
  BEGIN(INITIAL);
  lc++;
  ignore_line();
}
<LINEMARK_FILE>{STR} {
  size_t size;

  BEGIN(LINEMARK_FLAGS);
  lc += strlen(yytext);
  parsing_file = ALLOC_N(char, strlen(yytext)-1);
  size = strchr(yytext+1, '"') - yytext;
  strncpy(parsing_file, yytext+1, size);
  parsing_file[size-1] = 0;
}
<LINEMARK_FILE>[ \t\v\f]+ {
  lc += strlen(yytext);
}
<LINEMARK_FILE>. {
  BEGIN(INITIAL);
  lc++;
  ignore_line();
}
<LINEMARK_FLAGS>{D} {
  int flag;

  lc += strlen(yytext);
  flag = strtol(yytext, NULL, 10);
  switch (flag) {
  case 1:
    line_mark_flags |= LINE_MARK_BEGIN;
    break;
  case 2:
    line_mark_flags |= LINE_MARK_END;
    break;
  case 3:
    line_mark_flags |= LINE_MARK_SYSTEM;
    break;
  case 4:
    line_mark_flags |= LINE_MARK_SRC;
    break;
  }
}
<LINEMARK_FLAGS>\n {
  BEGIN(INITIAL);
  lc += strlen(yytext);
  objc_record_line_mark(parsing_lineno, parsing_file, line_mark_flags);
  if ((line_mark_flags == 0 || line_mark_flags & LINE_MARK_BEGIN) &&
      (objc_current_line_mark == NULL ||
       !STREQ(objc_current_line_mark->file, parsing_file)))
    objc_push_line_mark(parsing_lineno, parsing_file);
  else if (line_mark_flags & LINE_MARK_END)
    objc_pop_line_mark();
  else
    objc_current_line_mark->lineno = parsing_lineno;
}
<LINEMARK_FLAGS>[ \t\v\f]+ {
  lc += strlen(yytext);
}
<LINEMARK_FLAGS>. {
  BEGIN(INITIAL);
  lc++;
  ignore_line();
}

"/*"                    { comment(); }
"//"                    { comment_one_line(); }

"__abstract__"          { count(); return ABSTRACT; }
"asm"                   { count(); return GCC_ASM; }
"__asm"                 { count(); return GCC_ASM2; }
"__asm__"               { count(); return GCC_ASM3; }
"__attribute__"         { count(); return GCC_ATTR; }
"auto"                  { count(); return AUTO; }
"break"                 { count(); return BREAK; }
"case"                  { count(); return CASE; }
"char"                  { count(); return CHAR; }
"const"                 { count(); return CONST; }
"__const"               { count(); return CONST2; }
"__const__"             { count(); return CONST3; }
"continue"              { count(); return CONTINUE; }
"default"               { count(); return DEFAULT; }
"do"                    { count(); return DO; }
"double"                { count(); return DOUBLE; }
"else"                  { count(); return ELSE; }
"enum"                  { count(); return ENUM; }
"__extension__"         { count(); return EXTENSION; }
"extern"                { count(); return EXTERN; }
"float"                 { count(); return FLOAT; }
"for"                   { count(); return FOR; }
"__FUNCTION__"          { count(); return FUNCTION; }
"goto"                  { count(); return GOTO; }
"if"                    { count(); return IF; }
"inline"                { count(); return INLINE; }
"__inline"              { count(); return INLINE2; }
"__inline__"            { count(); return INLINE3; }
"Instance"              { count(); yylval.range.v.len = strlen("Instance"); return
 INSTANCE; }
"int"                   { count(); return INT; }
"long"                  { count(); return LONG; }
"__public__"            { count(); return PUBLIC_QUAL; }
"__protected__"         { count(); return PROTECTED_QUAL; }
"__private__"           { count(); return PRIVATE_QUAL; }
"register"              { count(); return REGISTER; }
"return"                { count(); return RETURN; }
"__PRETTY_FUNCTION__"   { count(); return PRETTY_FUNCTION; }
"__sealed__"            { count(); return SEALED; }
"Self"                  { count(); yylval.range.v.len = strlen("Self"); return SELF; }
"short"                 { count(); return SHORT; }
"signed"                { count(); return SIGNED; }
"__signed"              { count(); return SIGNED2; }
"__signed__"            { count(); return SIGNED3; }
"sizeof"                { count(); return SIZEOF; }
"super"                 { count(); return SUPER; }
"static"                { count(); return STATIC; }
"struct"                { count(); return STRUCT; }
"switch"                { count(); return SWITCH; }
"typedef"               { count(); return TYPEDEF; }
"union"                 { count(); return UNION; }
"unsigned"              { count(); return UNSIGNED; }
"void"                  { count(); return VOID; }
"volatile"              { count(); return VOLATILE; }
"__volatile"            { count(); return VOLATILE2; }
"__volatile__"          { count(); return VOLATILE3; }
"while"                 { count(); return WHILE; }
"@category"             { count(); yylval.range.v.len = strlen("@category"); return CATEGORY; }
"@class"                { count(); yylval.range.v.len = strlen("@class"); return CLASS; }
"@compatibility_alias"  { count(); yylval.range.v.len = strlen("@compatibility_alias"); return COMPATIBILITY_ALIAS; }
"@encode"               { count(); yylval.range.v.len = strlen("@encode"); return ENCODE; }
"@end"                  { count(); yylval.range.v.len = strlen("@end"); return END; }
"@interface"            { count(); yylval.range.v.len = strlen("@interface"); return INTERFACE; }
"@implementation"       { count(); yylval.range.v.len = strlen("@implementation"); return IMPL; }
"@private"              { count(); yylval.range.v.len = strlen("@private"); return PRIVATE; }
"@protected"            { count(); yylval.range.v.len = strlen("@protected"); return PROTECTED; }
"@protocol"             { count(); yylval.range.v.len = strlen("@protocol"); return PROTOCOL; }
"@public"               { count(); yylval.range.v.len = strlen("@public"); return PUBLIC; }
"@selector"             { count(); yylval.range.v.len = strlen("@selector"); return SEL; }

{L}({L}|{D})*           { count(); return check_type(); }

0[xX]{H}+{IS}?          { count(); yylval.range.v.s = objc_strdup(yytext); return INT_CONST; }
0{D}+{IS}?              { count(); yylval.range.v.s = objc_strdup(yytext); return INT_CONST; }
{D}+{IS}?               { count(); yylval.range.v.s = objc_strdup(yytext); return INT_CONST; }
{CHAR}                  { count(); yylval.range.v.s = objc_strdup(yytext); return CHAR_CONST; }

{D}+{E}{FS}?            { count(); yylval.range.v.s = objc_strdup(yytext); return FLOAT_CONST; }
{D}*"."{D}+({E})?{FS}?  { count(); yylval.range.v.s = objc_strdup(yytext); return FLOAT_CONST; }
{D}+"."{D}*({E})?{FS}?  { count(); yylval.range.v.s = objc_strdup(yytext); return FLOAT_CONST; }

({STR}{SPACE}*)+        { count(); yylval.range.v.s = objc_strdup(yytext); return STR_LIT; }
(@{STR}{SPACE}*)+       { count(); yylval.range.v.s = objc_strdup(yytext); return XSTR_LIT; }

">>="                   { count(); return RIGHT_ASSIGN_OP; }
"<<="                   { count(); return LEFT_ASSIGN_OP; }
"+="                    { count(); return ADD_ASSIGN_OP; }
"-="                    { count(); return SUB_ASSIGN_OP; }
"*="                    { count(); return MUL_ASSIGN_OP; }
"/="                    { count(); return DIV_ASSIGN_OP; }
"%="                    { count(); return MOD_ASSIGN_OP; }
"&="                    { count(); return AND_ASSIGN_OP; }
"^="                    { count(); return XOR_ASSIGN_OP; }
"|="                    { count(); return OR_ASSIGN_OP; }
">>"                    { count(); return RIGHT_OP; }
"<<"                    { count(); return LEFT_OP; }
"++"                    { count(); return INC_OP; }
"--"                    { count(); return DEC_OP; }
"->"                    { count(); return PTR_OP; }
"&&"                    { count(); return LAND_OP; }
"||"                    { count(); return LOR_OP; }
"<="                    { count(); return LE_OP; }
">="                    { count(); return GE_OP; }
"=="                    { count(); return EQ_OP; }
"!="                    { count(); return NE_OP; }
";"                     { count(); objc_end_decl_spcr_list(); return SEMI; }
"{"                     { count(); return LBRACE; }
"}"                     { count(); return RBRACE; }
","                     { count(); objc_end_decl_spcr_list(); return COMMA; }
":"                     { count(); return COLON; }
"::"                    { count(); return COLON2; }
"="                     { count(); return ASSIGN_OP; }
"("                     { count(); objc_end_decl_spcr_list(); return LPAREN; }
")"                     { count(); return RPAREN; }
"["                     { count(); objc_end_decl_spcr_list(); return LBRACK; }
"]"                     { count(); return RBRACK; }
"."                     { count(); return PERIOD; }
"..."                   { count(); return ELLIPSIS; }
"&"                     { count(); return AND_OP; }
"!"                     { count(); return LNOT_OP; }
"~"                     { count(); return NOT_OP; }
"-"                     { count(); return MINUS_OP; }
"+"                     { count(); return PLUS_OP; }
"*"                     { count(); objc_end_decl_spcr_list(); return TIMES_OP; }
"/"                     { count(); return DIV_OP; }
"%"                     { count(); return MOD_OP; }
"<"                     { count(); return LT_OP; }
">"                     { count(); return GT_OP; }
"^"                     { count(); return XOR_OP; }
"|"                     { count(); return OR_OP; }
"?"                     { count(); return '?'; }

[ \t\v\n\f]             { count(); }
.                       { /* ignore bad characters */ }

%%

void
objc_init_scan()
{
  parsing_file = NULL;
  parsing_lineno = 0;
  line_mark_flags = 0;
  lc = 0;
}

static void
ignore_line()
{
  char c;

  if (*yytext != '\n') {
    while ((c = input()) != '\n' && c != 0)
      lc++;
    lc++;
  }
}

int yywrap()
{
  return 1;
}

static void comment()
{
  char c;

loop:
  while ((c = input()) != '*' && c != 0) {
    if (c == '\n') {
      if (objc_current_line_mark != NULL)
        objc_current_line_mark->lineno++;
      objc_parsing_column = 0;
    } else
      objc_parsing_column++;
  }
  if ((c = input()) != '/' && c != 0) {
    if (c == '\n') {
      if (objc_current_line_mark != NULL)
        objc_current_line_mark->lineno++;
      objc_parsing_column = 0;
    } else
      objc_parsing_column++;
    goto loop;
  }
}

static void comment_one_line()
{
  char c;

  while ((c = input()) != '\n') {
    lc++;
    objc_parsing_column++;
  }
}

static void count()
{
  int i;
  size_t len;

  len = strlen(yytext);
  yylval.range.lc = lc;
  yylval.range.v.len = len;
  lc += len;

  for (i = 0; yytext[i] != '\0'; i++)
    if (yytext[i] == '\n') {
      if (objc_current_line_mark != NULL)
        objc_current_line_mark->lineno++;
      objc_parsing_column = 0;
    } else if (yytext[i] == '\t')
      objc_parsing_column += 8 - (objc_parsing_column % 8);
    else
      objc_parsing_column++;
}

static int check_type()
{
  objc_scope_entry_t entry = NULL;

  /* includes ':' for keyword selector */ 
  yylval.range.v.s = ALLOC_ATOM_N(char, strlen(yytext)+2);
  strcpy(yylval.range.v.s, yytext);

  entry = objc_scope_get_type(objc_current_scope, yytext);
  if (entry != NULL) {
    switch (entry->type) {
    case OBJC_TYPE_NAME:
      if (objc_in_decl_spcr_list) {
        objc_end_decl_spcr_list();
        return IDENTIFIER;
      } else {
        objc_begin_decl_spcr_list();
        return TYPE_NAME;
      }
    case OBJC_COMPILER_BUILTIN:
      return TYPE_NAME;
    case OBJC_CLASS_NAME:
      return CLASS_NAME;
    case OBJC_VAR_NAME:
    case OBJC_SELF:
      return IDENTIFIER;
    default:
      objc_print_error("check_type(): unknown type table entry type %d",
                        entry->type);
      return 0;
    }
  } else {
    objc_end_decl_spcr_list();
    st_lookup(objc_declared_class_table, yytext, (st_data_t *)&entry);
    if (entry != NULL)
      return CLASS_NAME;
    else
      return IDENTIFIER;
  }
}

